package com.abel.bigwater.dataserver.model.hgd

import io.netty.buffer.ByteBuf
import kotlin.experimental.and

abstract class HgdChannel<T>(_type: HgdChannelType, _count: Short) {

    var type: HgdChannelType = _type

    var count: Short = _count

    /**
     * the channel to set
     */
    var channel: Short? = null

    /**
     * reading of yesterday.
     */
    var yest: T? = null

    /**
     * the values to set
     */
    var values: ArrayList<T> = ArrayList()

    /**
     * Channel id, format as 'P1'/P2/A5/etc.
     */
    var channelId: String? = null
        get() = "${type.abbr}$channel"
        private set

    init {
        if (count < 0) {
            throw IllegalArgumentException("count should be positive but got: $type, $count, ${values.size}")
        }
    }

    /**
     * parse the buffer, got entire channel will return true & consume bytes;
     * bytes will not be consumed if return false.
     */
    open fun parseBuffer(buf: ByteBuf): Boolean {
        val len = bufLength()
        if (buf.readableBytes() < len) {
            return false
        }

        val typeId = buf.readByte()
        if (typeId != type.num) {
            throw IllegalArgumentException("please check channel type is same as: $typeId")
        }

        channel = buf.readByte().toShort() and 0xFF

        return true
    }

    /**
     * compute length of bytes.
     */
    abstract fun bufLength(): Int

    /**
     * verify fields and pack to buf, return true always.
     */
    open fun pack(buf: ByteBuf): Boolean {
        if (channel == null || values.size != count.toInt()) {
            throw IllegalArgumentException(" can't be null: $type/$channel and ${values.size}?=$count.")
        }

        buf.writeByte(type.num.toInt())
        buf.writeByte(channel!!.toInt())

        return true
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    override fun toString() = "$channelId[$yest#${values.joinToString()}]"

    companion object {
        /**
         * just parse abbr but do not disturb buf's readerIndex.
         */
        fun parseType(buf: ByteBuf): HgdChannelType? {
            if (buf.readableBytes() < 1) return null

            return HgdChannelType.findByNum(buf.duplicate().readByte())
        }
    }
}

